The shared UI layer — what every brand must implement to feel like a Dunwell company. Personality, recognition, and craft, codified.
A customer who uses Aptly, then discovers Tacet, should feel something familiar before they can name it. These eight elements are the connective tissue — each one is either required or strongly recommended across all brands.
Every Dunwell brand has a wordmark. What that wordmark looks like is entirely up to the brand — typeface, weight, color, style. What it must contain is non-negotiable. The system standardizes the rules. The brand owns the expression. Invio being brutalist yellow is not a deviation — it's the point. Each brand was built for a different audience, and the mark should reflect that.
Three typefaces. Every brand uses all three. The combination is the signature — editorial serif for brand moments, clean sans for UI, mono for metadata and code. Never substitute.
/* Required Google Fonts import — all brands */ @import url('https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,300..600;1,6..72,300..600&family=Hanken+Grotesk:wght@300..700&family=JetBrains+Mono:wght@400;500;600&display=swap'); /* Required CSS variables */ :root { --serif: 'Newsreader', Georgia, serif; --sans: 'Hanken Grotesk', system-ui, sans-serif; --mono: 'JetBrains Mono', monospace; }
Each brand owns one accent color. That accent becomes --accent in their CSS. The seasonal engine then overrides it with --season-accent for contextual personality. Making --accent: var(--season-accent, [default]) wires both at once.
Every primary and ghost button carries two effects: a monochromatic gradient (subtle tonal depth, always baked in) and the sheen animation (diagonal light sweep on hover). Same keyframe, same easing across every brand. The gradient changes with the accent color automatically — no extra work.
.btn-primary {
background: linear-gradient(
160deg,
color-mix(in srgb, var(--accent) 85%, white),
var(--accent) 50%,
color-mix(in srgb, var(--accent) 85%, black)
);
}
/* ── Shared sheen animation — paste into every brand ── */ @keyframes dw-shine { to { left: 120% } } /* Primary */ .btn-primary { font-family: var(--mono); font-size: 10px; font-weight: 500; letter-spacing: .15em; text-transform: uppercase; background: var(--accent); color: var(--bg); padding: 13px 28px; border: none; cursor: pointer; position: relative; overflow: hidden; } .btn-primary::after { content: ""; position: absolute; top: 0; bottom: 0; left: -65%; width: 40%; background: linear-gradient(105deg, transparent, rgba(255,255,255,.3), transparent); transform: skewX(-18deg); pointer-events: none; } .btn-primary:hover::after { animation: dw-shine .65s ease-out; } .btn-primary:hover { opacity: .88; }
The engine lives at shared/dunwell-seasons.js. Load it before your brand script. Each brand passes its own accent color map — the natural season color filtered through the brand's palette. Append ?season=christmas to any URL to test any event instantly.
<!-- 1. Load engine in <head> --> <script src="../shared/dunwell-seasons.js"></script> <!-- 2. Init immediately after --> <script> DunwellSeasons.init({ accentMap: DunwellSeasons.BRAND_MAPS.graft, // aptly | tacet | graft | lucro | invio themeKey: 'graft_theme', darkDefault: true, }); </script> /* 3. In CSS — wire accent to season */ :root { --accent: var(--season-accent, #9E6B38); }
Consistent easing and timing makes brands feel related even when they look different. These values are the Dunwell timing standard — copy them verbatim.
Required: at least one hidden interaction per brand. They reward curiosity, signal craft, and build a shared Dunwell reputation. The trigger should be brand-appropriate — not random. The payoff should feel earned.
Every item marked Required must be complete before a brand goes live. Recommended items should be planned and scheduled. This checklist applies to every brand under the Dunwell holding group.